/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.reflect;

import java.lang.reflect.*;
import java.util.Arrays;

import static org.vacoor.nothing.common.reflect.TypeFactory.*;

/**
 * TODO CACHE
 * 泛型类型参数解析工具
 * <p/>
 * Java 中泛型类型局部变量的泛型参数会被擦除掉,
 * 但是类级别(静态), 实例级别的变量,方法,参数以及类定义上的类型参数都不会被擦除(匿名类属于类定义)
 *
 * @author vacoor
 */
@SuppressWarnings("unused")
public abstract class TypeResolver {
    /**
     * 在给定泛型类型参数解析上下文中, type 类型是否能接受 actualType 类型的值
     *
     * @param contextType  泛型类型参数解析上下文
     * @param acceptedType 接受的类型
     * @param actualType   实际的类型
     * @return
     */
    public static boolean canAcceptType(final Class<?> contextType, Type acceptedType, final Class<?> actualType) {
        acceptedType = TypeResolver.resolveType(contextType, acceptedType);

        return new TypeSwitch<Boolean>() {
            @Override
            public Boolean caseClass(Class<?> classType) {
                return classType.isAssignableFrom(actualType);
            }

            @Override
            public Boolean caseGenericArrayType(GenericArrayType genericArrayType) {
                return !actualType.isArray() ? false : canAcceptType(contextType, genericArrayType.getGenericComponentType(), actualType.getComponentType());
            }

            @Override
            public Boolean caseTypeVariable(TypeVariable typeVariable) {
                Type[] bounds = typeVariable.getBounds();
                for (Type bound : bounds) {
                    if (!canAcceptType(contextType, bound, actualType)) {
                        return false;
                    }
                }
                return true;
            }

            @Override
            public Boolean caseWildcardType(WildcardType wildcardType) {
                Type[] lowerBounds = wildcardType.getLowerBounds();
                Type[] upperBounds = wildcardType.getUpperBounds();
                for (Type upperBound : upperBounds) {
                    if (!canAcceptType(contextType, upperBound, actualType)) {
                        return false;
                    }
                }
                for (Type lowerBound : lowerBounds) {
                    if (!(lowerBound instanceof Class<?>) || !canAcceptType(contextType, actualType, (Class<?>) lowerBound)) {
                        return false;
                    }
                }
                return true;
            }

            @Override
            public Boolean caseParameterizedType(ParameterizedType parameterizedType) {
                Type rawType = parameterizedType.getRawType();

                if (rawType instanceof Class<?>) {
                    // 将当前参数化类型在上下文中解析(主要为了替换泛型类型参数, 因为两个类型参数如果定义类不同则 equals = false)
                    parameterizedType = (ParameterizedType) TypeResolver.resolveType(actualType, parameterizedType);
                    // 获取参数在当前候选对象中的泛型参数类型
                    Type[] actualTypeArgs = TypeResolver.resolveActualTypeArguments(actualType, (Class<?>) rawType);
                    if (Arrays.equals(parameterizedType.getActualTypeArguments(), actualTypeArgs)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public Boolean defaultCase(Type t) {
                return false;
            }
        }.doSwitch(acceptedType);
    }

    /**
     * 获取 type 在 TypeReference 引用的上下文中解析类型参数后的类型
     * {@link #resolveType(java.lang.reflect.Type, java.lang.reflect.Type)}
     *
     * @param contextTypeRef 泛型类型解析上下文类型引用
     * @param type           需要泛型类型参数的类型
     * @return
     */
    public static Type resolveType(final TypeReference<?> contextTypeRef, final Type type) {
        return resolveType(contextTypeRef.getType(), type);
    }

    /**
     * 获取 type 在给定泛型类型上下文 context 中解析类型参数后类型
     * <p/>
     * <pre>
     * 1. resolveType(new ArrayList<String>(){}.getClass(), List.class); --> java.util.List<String>
     * 2.
     * class A<T> {
     *     public T type;
     *     public List<T> list;
     * }
     * class B<K,V> extends A<V>{
     *     public K key;
     * }
     * class C extends B<String, Integer>{}
     *
     * resolveType(B.class, A.class.getField("type").getGenericType()) --> V
     * resolveType(C.class, A.class.getField("type").getGenericType()) --> java.lang.Integer
     * resolveType(C.class, A.class.getField("list").getGenericType()) --> java.util.List<Integer>
     * resolveType(C.class, B.class.getField("key").getGenericType())    --> java.lang.String
     * </pre>
     *
     * @param contextType 泛型类型解析上下文类型
     * @param type        需要泛型类型参数的类型
     * @return
     */
    public static Type resolveType(final Type contextType, final Type type) {
        return new TypeSwitch<Type>() {
            @Override
            public Type caseClass(Class<?> classType) {
                return resolveType(classType, classType.getTypeParameters(), type);
            }

            @Override
            public Type caseParameterizedType(ParameterizedType parameterizedType) {
                Type rawType = parameterizedType.getRawType();
                return rawType instanceof Class<?> ? resolveType(
                        (Class<?>) rawType,
                        parameterizedType.getActualTypeArguments(),
                        type
                ) : defaultCase(parameterizedType);
            }

            @Override
            public Type defaultCase(Type t) {
                return t;
            }
        }.doSwitch(contextType);
    }

    /**
     * 获取 type 在给定泛型类型上下文中解析类型参数后类型
     *
     * @param contextType           泛型类型解析上下文类型
     * @param contextActualTypeArgs 泛型类型解析上下文的实际类型参数
     * @param type                  需要泛型类型参数的类型
     * @param type
     * @return
     */
    static Type resolveType(final Class<?> contextType, final Type[] contextActualTypeArgs, final Type type) {
        return new TypeSwitch<Type>() {
            @Override
            public Type caseClass(Class<?> classType) {
                TypeVariable<? extends Class<?>>[] typeParams = classType.getTypeParameters();
                return 0 < typeParams.length ? resolveType(contextType, contextActualTypeArgs, createParameterizedType(
                        classType,
                        typeParams,
                        null
                )) : classType;
            }

            @Override
            public Type caseTypeVariable(TypeVariable typeVariable) {
                GenericDeclaration declaration = typeVariable.getGenericDeclaration();
                TypeVariable<?>[] typeParameters = declaration.getTypeParameters();
                Class<?> declarationContext = (Class<?>) declaration;
                Type[] resolvedTypes = //!declarationContext.isAssignableFrom(context)
                        // ? contextActualTypeArgs :
                        resolveActualTypeArguments(contextType, contextActualTypeArgs, declarationContext);

                for (int i = 0; i < typeParameters.length; i++) {
                    if (typeParameters[i].equals(typeVariable)) {
                        return resolvedTypes[i];
                    }
                }
                return null;
            }

            @Override
            public Type caseParameterizedType(ParameterizedType parameterizedType) {
                return createParameterizedType(
                        parameterizedType.getRawType(),
                        resolveTypes(contextType, contextActualTypeArgs, parameterizedType.getActualTypeArguments()),
                        parameterizedType.getOwnerType()
                );
            }

            @Override
            public Type caseGenericArrayType(GenericArrayType genericArrayType) {
                return createArrayType(resolveType(contextType, contextActualTypeArgs, genericArrayType.getGenericComponentType()));
            }

            @Override
            public Type caseWildcardType(WildcardType wildcardType) {
                Type[] upperBounds = resolveTypes(contextType, contextActualTypeArgs, wildcardType.getUpperBounds());
                Type[] lowerBounds = resolveTypes(contextType, contextActualTypeArgs, wildcardType.getLowerBounds());
                return createWildcardType(upperBounds, lowerBounds);
            }

            @Override
            public Type defaultCase(Type t) {
                return t;
            }
        }.doSwitch(type);
    }

    static Type[] resolveTypes(Class<?> contextType, Type[] actualTypeArgs, Type... types) {
        Type[] resolvedTypes = new Type[types.length];
        for (int i = 0; i < types.length; i++) {
            resolvedTypes[i] = resolveType(contextType, actualTypeArgs, types[i]);
        }
        return resolvedTypes;
    }

    /**
     * 获取 Array, Iterable 解析泛型类型参数后的元素类型, 无法解析或其他类型返回null
     * <p/>
     * resolveComponentType(new TypeReference<List<String>>(){}.getType()); --> java.lang.String
     *
     * @param type 需要解析的 Iterable, Array 类型
     * @return
     */
    public static Class<?> resolveComponentClass(Type type) {
        type = resolveComponentType(type);
        return type instanceof Class<?> ? (Class<?>) type : null;
    }

    /**
     * 获取 Array, Iterable 解析泛型类型参数后的元素类型, 无法解析或其他类型返回null
     *
     * @param type 需要解析的 Iterable, Array 类型
     * @return
     */
    public static Type resolveComponentType(Type type) {
        return new TypeSwitch<Type>() {
            @Override
            public Type caseClass(Class<?> classType) {
                if (classType.isArray()) {
                    return classType.getComponentType();
                }
                if (Iterable.class.isAssignableFrom(classType)) {
                    return resolveActualTypeArguments(classType, Iterable.class, 0);
                }
                return null;
            }

            @Override
            public Type caseParameterizedType(ParameterizedType parameterizedType) {
                Type rawType = parameterizedType.getRawType();
                if (rawType instanceof Class<?> && Iterable.class.isAssignableFrom((Class<?>) rawType)) {
                    return resolveActualTypeArguments((Class<?>) rawType, parameterizedType.getActualTypeArguments(), Iterable.class, 0);
                }
                return null;
            }
        }.doSwitch(type);
    }

    public static Type resolveActualTypeArguments(Class<?> context, Class<?> genericSuper, int index) {
        Type[] actualTypes = resolveActualTypeArguments(context, genericSuper);
        return index < actualTypes.length ? actualTypes[index] : null;
    }

    public static Type resolveActualTypeArguments(Class<?> context, Type[] contextActualTypeArgs, Class<?> genericSuper, int index) {
        Type[] actualTypes = resolveActualTypeArguments(context, contextActualTypeArgs, genericSuper);
        return index < actualTypes.length ? actualTypes[index] : null;
    }

    /**
     * 获取泛型超类在给定子类上下文的实际类型参数
     */
    public static Type[] resolveActualTypeArguments(Class<?> context, Class<?> target) {
        return resolveActualTypeArguments(context, context.getTypeParameters(), target);
    }

    /**
     * 获取泛型超类在给定子类上下文的实际类型参数
     * <p/>
     * <pre>
     *     class A<V> {}
     *     class B<K,V> extends A<V>{}
     *     class C extends B<String, Integer>{}
     *
     *     resolveActualTypeArguments(B.class, B.class.getTypeParameters(), A.class)  --> V
     *     (注: 这个V定义类为B.class, 和通过A的 ParameterizedType#getActualTypeArguments() 获取到的 V 定义类为A.class, 是不同的 equals = false)
     *     resolveActualTypeArguments(C.class, C.class.getTypeParameters(), A.class)  --> Integer
     *     resolveActualTypeArguments(C.class, C.class.getTypeParameters(), A.class)  --> Integer
     * </pre>
     *
     * @param contextRawType             解析上下文原始类型
     * @param contextActualTypeArguments 解析上下文实际类型参数
     * @param genericSuperType           泛型超类/接口
     */
    public static Type[] resolveActualTypeArguments(Class<?> contextRawType, Type[] contextActualTypeArguments, Class<?> genericSuperType) {
        if (!isInterfaceOrClass(contextRawType) || !isInterfaceOrClass(genericSuperType)) {
            throw new IllegalArgumentException("context and genericSuperclass is only an interface or class");
        }
        // 如果 context 没有继承/实现 超类, 或者不存在类型参数
        if (!genericSuperType.isAssignableFrom(contextRawType) || 1 > genericSuperType.getTypeParameters().length) {
            return genericSuperType.getTypeParameters();
        }
        // 找到目标
        if (contextRawType == genericSuperType) {
            return contextActualTypeArguments;
        }

        Type superType = contextRawType.getGenericSuperclass();
        // 1. 如果上下文为接口, 超类一定是接口
        // 2. 如果没有找到目标, 当超类是Object, 可能是直接实现了接口
        // 3. 如果超类不适目标的子类, 此时目标一定是接口, 可能是中间类实现了该接口
        if (contextRawType.isInterface() || (superType == Object.class) || (!genericSuperType.isAssignableFrom(contextRawType.getSuperclass()))) {
            // 查找任意一个实现目标接口的父接口
            // 有可能存在多个父接口都实现了目标接口, 但他们的对目标的泛型参数是必须一样的, 所以任意选取一个
            Type[] interfaces = contextRawType.getGenericInterfaces();
            for (Type ifc : interfaces) {
                Type t = (ifc instanceof ParameterizedType) ? ((ParameterizedType) ifc).getRawType() : ifc;
                if (t instanceof Class<?> && genericSuperType.isAssignableFrom((Class<?>) t)) {
                    superType = ifc;
                    break;
                }
            }
        }

        // 如果超类是 class 则清空实际类型参数后继续查找 (eg: class B<E> extends A {}, A 为class)
        if (superType instanceof Class<?>) {
            return resolveActualTypeArguments((Class<?>) superType, new Type[0], genericSuperType);
        }

        // 如果是参数化类型
        if (superType instanceof ParameterizedType) {
            TypeVariable<? extends Class<?>>[] contextTypeParams = contextRawType.getTypeParameters();  // 当前上下文类型参数
            Type[] superActualTypeArgs = ((ParameterizedType) superType).getActualTypeArguments();      // 父类的实际参数

            // 根据子类声明的类型变量与父类类型变量关系进行实参替换
            // eg: A extends B<String>, B<T> extends C<T> 则将 C<T> 替换为 C<String>
            for (int i = 0; i < superActualTypeArgs.length; i++) {
                Type superActualTypeArg = superActualTypeArgs[i];

                // 如果超类实参是类型变量
                if (superActualTypeArg instanceof TypeVariable<?>) {
                    TypeVariable<?> superTypeParam = (TypeVariable<?>) superActualTypeArg;

                    // 将超类的类型变量替换对应子类类型变量的实参值
                    for (int j = 0; j < contextTypeParams.length && j < contextActualTypeArguments.length; j++) {
                        if (superTypeParam.equals(contextTypeParams[j])) {  // 只有在同一个类定义且名称上限,名称相同才相等
                            superActualTypeArgs[i] = contextActualTypeArguments[j];
                        }
                    }
                }
            }

            Class<?> superclass = contextRawType.getSuperclass();
            if (contextRawType.isInterface() || (superclass == Object.class) || !genericSuperType.isAssignableFrom(superclass)) {
                Class<?>[] interfaces = contextRawType.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    if (genericSuperType.isAssignableFrom(anInterface)) {
                        superclass = anInterface;
                        break;
                    }
                }
            }
            return resolveActualTypeArguments(superclass, superActualTypeArgs, genericSuperType);
        }

        throw new UnsupportedOperationException();
    }

    private static boolean isInterfaceOrClass(Class<?> clazz) {
        return !(clazz.isAnnotation() || clazz.isArray() || clazz.isEnum() || clazz.isPrimitive());
    }

    private TypeResolver() {
    }
}
